<?php


namespace Cblood\Route;

use Cblood\Container\Annotation\Annotation;
use Cblood\Container\Annotation\ReflectionAnnotationClass;
use Cblood\Container\Overall;
use Cblood\Route\Http\HttpResponseResult;
use Cblood\Route\Realize\Controller;
use Cblood\Route\Http\HttpRequest;
use Cblood\Route\Http\HttpResponse;
use Cblood\Route\Realize\RequestMethodLabel;

class Route
{
    protected $request;

    protected $response;

    /**
     * 慎用！
     * 是否进行动态uri解析
     * 可以用 index/{id} 的形式
     * 作用：访问 url：index，{id}是一个动态值，从 $_GET['id'] 获取值
     * 警告：大量使用此方式，运行速度将大幅下降,慎用！
     * 默认是不打开的
     * @var bool
     */
    protected $isDynamicMap = false;

    /**
     * 请求配置项
     * 格式 array(
     *      'index' => Controller::class
     * )
     * @var array
     */
    protected $routeMap = [];

    /**
     * 请求配置项
     * 格式 array(
     *      'index/{id}' => Controller::class
     * )
     * @var array
     */
    protected $routeDynamicMap = [];

    /**
     * 默认使用的标签
     * @var array
     */
    protected $defaultLabel = 'Method';

    protected $defaultLabelClass = RequestMethodLabel::class;

    protected $defaultController;

    public function __construct(HttpRequest $request, HttpResponse $response)
    {
        $this->request = $request;
        $this->response = $response;
        Annotation::register([$this->defaultLabel => $this->defaultLabelClass]);
    }

    public function routeMap(array $routeMap)
    {
        $this->routeMap = array_merge($this->routeMap, $routeMap);
    }

    /**
     * 执行路由，并返回结果
     * @return HttpResponseResult|mixed
     * @throws \Exception
     */
    public function execute()
    {
        $this->ruleRequestParameters($_GET);
        $this->ruleRequestParameters($_POST);
        $this->ruleRequestParameters($_REQUEST);

        /**
         * 找 uri 对应的 Controller
         * @var Controller $controller
         */
        $controllerName = $this->selectRouteMap();
        $controller = Overall::container()->instance($controllerName);
        $responseResult = NULL;
        if ($controller instanceof Controller) {            //实现了 Controller 的接口，直接执行相对应的方法
            switch ($this->request->method()) {
                case 'GET' :
                    $responseResult = $controller->requestGet($this->request, $this->response);
                    break;
                case 'PUT' :
                    $responseResult = $controller->requestPut($this->request, $this->response);
                    break;
                case 'POST':
                    $responseResult = $controller->requestPost($this->request, $this->response);
                    break;
                case 'DELETE':
                    $responseResult = $controller->requestDelete($this->request, $this->response);
                    break;
                default:
                    throw new \Exception('无效的请求方式！');
            }
        } else {                //没有实现 Controller 接口的,以标签 Request 传入的 method 执行相应方法
            $functionName = $this->findControllerMethod($controllerName);
            if (empty($functionName)) {
                throw new \Exception('找不到对象！');
            }

            $responseResult = Overall::container()->callMethod($controller, $functionName);
        }

        $this->response->setDefaultResponse();
        return $responseResult;
    }

    /**
     * 根据 uri 选择需要执行的 controller
     * @return Controller|object
     * @throws \Exception
     */
    protected function selectRouteMap()
    {
        $uri = $this->request->baseUri();
        $uri = $uri == '/' ? $uri : trim($uri, '/');
        if (array_key_exists($uri, $this->routeMap)) {
            return $this->routeMap[$uri];
        }

        //写不出正则，先暂缓
        if ($this->isDynamicMap) {
            foreach ($this->routeDynamicMap as $path => $controllerName) {
                $map = $this->analysisDynamicMap($path, $uri);
                if (!empty($map)) {
                    foreach ($map as $name => $value) {
                        $_GET[$name] = $value;
                    }
                    return $controllerName;
                }
            }
        }

        if (!empty($this->defaultController)) {
            return $this->defaultController;
        }

        throw new \Exception('找不到对象！');
    }

    /**
     * 解析动态url
     * @param $dynamicMap
     * @param $uri
     * @return array
     */
    protected function analysisDynamicMap($dynamicMap, $uri)
    {
        //用来装{}里面的值
        $params = [];
        $requestParams = explode('/', $dynamicMap);
        for ($i = 0; $i < count($requestParams); $i++) {
            if (strpos($requestParams[$i], '{') !== false && strpos($requestParams[$i], '}') !== false) {
                $temporary = trim($requestParams[$i], '{');
                $params[] = trim($temporary, '}');
            }
        }

        $patten = $dynamicMap;
        for ($i = 0; $i < count($params); $i++) {
            $patten = str_replace('{' . $params[$i] . '}', '(.*)', $patten);
        }

        $patten = str_replace('/', '\\/', $patten);
        $patten = '/' . $patten . '/';

        if (preg_match($patten, $uri, $matchResult)) {
            $result = [];
            array_shift($matchResult);
            for ($i = 0; $i < count($matchResult); $i++) {
                $result[$params[$i]] = $matchResult[$i];
            }

            return $result;
        }
        return [];
    }

    /**
     * 查找 controller 中所有的 function 是否有 $this->defaultLabel 标签
     * 且标签的输入值与 $this->request->method() 相同的方法名称
     * @param $className
     * @return string
     */
    protected function findControllerMethod($className)
    {
        $reflectionAnnotationClass = new ReflectionAnnotationClass(new \ReflectionClass($className));
        $annotationMethods = $reflectionAnnotationClass->getMethods();
        foreach ($annotationMethods as $annotationMethod) {
            $requestMethodLabel = $annotationMethod->getAnnotation($this->defaultLabel);
            if ($requestMethodLabel->execute() == $this->request->method()) {
                return $annotationMethod->getName();
            }
        }
        return null;
    }

    /**
     * 过滤请求参数
     * @param $array
     * @return mixed
     */
    protected function ruleRequestParameters(&$array)
    {
        while (list($key, $var) = each($array)) {
            if ($key != 'argc' && $key != 'argv' && (strtoupper($key) != $key || '' . intval($key) == "$key")) {
                if (is_string($var)) {
                    $array[$key] = stripslashes($var);
                }
                if (is_array($var)) {
                    $array[$key] = $this->ruleRequestParameters($var);
                }
            }
        }
        return $array;
    }

    /**
     * @param array $routeDynamicMap
     */
    public function routeDynamicMap(array $routeDynamicMap)
    {
        $this->routeDynamicMap = array_merge($this->routeDynamicMap, $routeDynamicMap);
    }

    /**
     * 默认不打开这种解析url的方式
     * @param bool $isDynamicMap
     */
    public function isDynamicMap($isDynamicMap = false)
    {
        $this->isDynamicMap = $isDynamicMap;
    }

    /**
     * 设置默认controller
     * 当 url 匹配不到 controller 时，将调用此 controller
     * 通常可以用作 404
     * @param string $defaultController 返回一个 controller 的名称
     */
    public function setDefaultController($defaultController)
    {
        $this->defaultController = $defaultController;
    }
}